
#include <BKeyEvent>
#include <BMouseEvent>
#include <BEvent>
#include <BTextEdit>
#include <BPainter>
#include <BCursor>
#include <BDrag>
#include <BSystem>
#include <BScreen>

#include "IAlignment.h"
#include "IWindow.h"
#include "IGlobal.h"
#include "member_BWidget.h"
#include "member_BObject.h"
#include "member_BEvent.h"
#include "member_BCursor.h"
#include "member_BShortcut.h"

using namespace BWE;

extern BReal app_clickInterval;
extern BList<IWindowHolder> app_windows;
extern BArray<GLuint>		UnusedTextureObjects;
extern BArray<GLuint>		UnusedFrameBufferObjects;
extern BArray<GLuint>		UnusedRenderBufferObjects;

IWindow::IWindow()
{
	boss = 0;
	close_time = 0;
	shown = false;
	resized = true;
	msfbo = 0;
	mstbo = 0;
	event_member(&event)->window = this;

	focusing = false;
	hoverWidget = 0;
	focusWidget = 0;
	
	mouse_button = Button_None;
	mouse_pressed = false;
	mouse_click_time = 0;
	keyStates.resize(Key_Max);

	refresh = false;
}
IWindow::~IWindow()
{

}

void IWindow::reset(BWidget* widget)
{
	boss = widget;
#ifdef linux
	if (display == 0)
#endif
#ifdef _WIN32
	if (hwnd == 0)
#endif
	{
		this->create();
	}
	else
	{
		this->setFrameless(widget->frameless());
		this->setWindowPos(widget->pos());
		this->setWindowSize(widget->size());
	}
}
void IWindow::update()
{
	event.reset(Event_Update);
	updateWidget(boss, event);
	if (tipsBoard.update())
	{
		refresh = true;
	}
	if (dragBoard.update())
	{
		refresh = true;
	}
}
void IWindow::fresh()
{
	event.reset(Event_Fresh);
	BSize maxSize;
	freshWidget(boss, maxSize);
	if (frameSize != maxSize)
	{
		frameSize = maxSize;
		resized = true;
	}
}
void IWindow::frame()
{
	if (refresh)
	{
		refresh = false;
		makeCurrent(true);
		BOpenGL::Init();
		int samples = widget_member(boss)->samples;
		if (samples > 0)
		{
			if (resized)
			{
				if (!msfbo)
				{
					glGenFramebuffers(1, &msfbo);
				}
				if (msfbo)
				{
					glBindFramebuffer(GL_FRAMEBUFFER, msfbo);
				}
				if (!mstbo)
				{
					glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
					glGenTextures(1, &mstbo);
				}
				if (mstbo)
				{
					glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mstbo);
					glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGBA, frameSize.width(), frameSize.height(), GL_TRUE);
					glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, mstbo, 0);
				}
				resized = false;
			}
		}
		else
		{
			if (msfbo)
			{
				UnusedFrameBufferObjects.append(msfbo);
				msfbo = 0;
			}
			if (mstbo)
			{
				UnusedTextureObjects.append(mstbo);
				mstbo = 0;
			}
		}

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		frameWidget(boss);
		glBindFramebuffer(GL_FRAMEBUFFER, 0);

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		glDisable(GL_LIGHTING);
		glEnable(GL_CULL_FACE);
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_MULTISAMPLE);

		glEnable(GL_BLEND);
		//glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
		glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
		glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
		glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);

		glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
		glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
		glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

		glDisable(GL_POLYGON_OFFSET_POINT);
		glDisable(GL_POLYGON_OFFSET_LINE);
		glDisable(GL_POLYGON_OFFSET_FILL);

		drawWindow();
		{
			BPainter painter(boss);
			drawCursor(painter);
		}
		drawDrag();
		drawTips();

		swapBuffers();
		BOpenGL::Release();
		makeCurrent(false);
	}
}

void IWindow::updateWidget(BWidget* widget, const BEvent& event)
{
	if (widget && widget_member(widget)->state.enabled)
	{
		widget_member(widget)->procEvent(event);
		for (int i = 0; i < widget->childCount(); i++)
		{
			BWidget* child = widget->child(i);
			updateWidget(child, event);
		}
	}
}
void IWindow::freshWidget(BWidget* widget, BSize& maxSize)
{
	if (widget == 0 || !widget_member(widget)->visible)
		return;
	if (widget_member(widget)->ignored())
		return;
	if (widget_member(widget)->refresh)
	{
		widget_member(widget)->refresh = false;
		widget_member(widget)->procEvent(event);
		widget_member(widget)->dirty = true;
		if (hoverWidget == widget)
		{
			tipsBoard.dirty = true;
		}
		refresh = true;
	}

	if (maxSize.width() < widget_member(widget)->size.width())
		maxSize.width() = widget_member(widget)->size.width();
	if (maxSize.height() < widget_member(widget)->size.height())
		maxSize.height() = widget_member(widget)->size.height();

	if (widget_member(widget)->dirtyLayouts)
	{
		widget_member(widget)->dirtyLayouts = false;
		widget_member(widget)->adjustDirtyLayouts();
	}
	for (int i = 0; i < widget->childCount(); i++)
	{
		BWidget* child = widget->child(i);
		freshWidget(child, maxSize);
	}
}
void IWindow::frameWidget(BWidget* widget)
{
	if (!widget_member(widget)->visible)
		return;
	if (!widget_member(widget)->state.shown)
		return;
	BWidgetHolderArray& children = widget_member(widget)->children;
	for (int i = children.size() - 1; i >= 0; i--)
	{
		BWidget* child = children[i];
		frameWidget(child);
	}
	if (widget_member(widget)->dirty)
	{
		widget_member(widget)->frame(msfbo, GL_COLOR_ATTACHMENT2);
	}
}

void IWindow::drawWindow()
{
	int width = widget_member(boss)->size.width();
	int height = widget_member(boss)->size.height();
	glViewport(0, 0, width, height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, width, height, 0, -1, 1);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glDisable(GL_CULL_FACE);

	glEnable(GL_TEXTURE_2D);
	BRect clip = boss->absoluteRect();
	drawWidget(boss, clip, BPoint(0, 0), clip.height());
	for (int i = 0; i < popupWidgets.size(); i++)
	{
		BWidget* widget = popupWidgets[i];
		drawPopupWidget(widget, clip, widget->absolutePos(), clip.height());
	}
	glBindTexture(GL_TEXTURE_2D, 0);
	glDisable(GL_TEXTURE_2D);
}
void IWindow::drawWidget(BWidget* widget, const BRect& clip, const BPoint& offset, int frameHeight)
{
	if (!widget_member(widget)->visible || popupWidgets.contain(widget))
		return;
	BSize size = widget->size();
	BRect ab_rect(offset, size);
	BRect view = ab_rect & clip;
	if (view.empty())
		return;

	glViewport(view.x(), frameHeight - view.bottom(), view.width(), view.height());
	BRect area(view.pos() - ab_rect.pos(), view.size());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(area.left(), area.right(), area.bottom(), area.top(), -1, 1);
	
	if (widget_member(widget)->tbo)
	{
		int width = widget_member(widget)->size.width();
		int height = widget_member(widget)->size.height();
		if (widget_member(widget)->opacity < 255)
		{
			glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
		}
		else
		{
			glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
		}
		glColor4ub(255, 255, 255, widget_member(widget)->opacity);
		glBindTexture(GL_TEXTURE_2D, widget_member(widget)->tbo);
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(0, 0);
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(0, height);
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(width, height);
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(width, 0);
		glEnd();
	}

	if (!widget_member(widget)->layered)
	{
		BRect ab_crect = widget->centerRect() + offset;
		BRect cview = ab_crect & clip;
		BWidgetHolderArray& children = widget_member(widget)->children;
		for (int i = 0; i < children.size(); i++)
		{
			BWidget* child = children[i];
			if (!widget_member(child)->visible)
				continue;
			if (widget_member(child)->ignored())
				continue;
			if (widget_member(child)->floating)
				drawWidget(child, view, offset + child->pos(), frameHeight);
			else
				drawWidget(child, cview, offset + child->pos(), frameHeight);
		}
	}
}
void IWindow::drawPopupWidget(BWidget* widget, const BRect& clip, const BPoint& offset, int frameHeight)
{
	if (!widget_member(widget)->visible)
		return;
	if (widget_member(widget)->ignored())
		return;
	BSize size = widget->size();
	BRect ab_rect(offset, size);
	BRect view = ab_rect & clip;
	if (view.empty())
		return;

	BRect ab_crect = widget->centerRect() + offset;
	BRect cview = ab_crect & clip;

	glViewport(view.x(), frameHeight - view.bottom(), view.width(), view.height());
	BRect area(view.pos() - ab_rect.pos(), view.size());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(area.left(), area.right(), area.bottom(), area.top(), -1, 1);

	if (widget_member(widget)->tbo)
	{
		int width = widget_member(widget)->size.width();
		int height = widget_member(widget)->size.height();
		glBindTexture(GL_TEXTURE_2D, widget_member(widget)->tbo);
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(0, 0);
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(0, height);
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(width, height);
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(width, 0);
		glEnd();
	}
	BWidgetHolderArray& children = widget_member(widget)->children;
	for (int i = 0; i < children.size(); i++)
	{
		BWidget* child = children[i];
		if (!widget_member(child)->visible)
			continue;
		if (widget_member(child)->ignored())
			continue;
		if (widget_member(child)->floating)
			drawWidget(child, view, offset + child->pos(), frameHeight);
		else
			drawWidget(child, cview, offset + child->pos(), frameHeight);
	}
}
void IWindow::drawCursor(BPainter& painter)
{
	if (!hoverWidget)
		return;
	BPoint globalPos = boss->globalPos();
	BPoint cursorPos = BCursor::GetPos();
	BPoint point = cursorPos - globalPos;

	BCursor* cursor = hoverWidget->cursor();
	if (cursor && cursor->type() == Cursor_None)
	{
		if (BImage* image = cursor->image())
		{
			BPoint origin = cursor->point();
			BSize size = cursor->size();
			if (size.width() < 1)
				size.width() = image->width();
			if (size.height() < 1)
				size.height() = image->height();
			if (cursor_member(cursor)->pointColor.a())
			{
				painter.setColor(cursor_member(cursor)->pointColor);
				painter.drawPoint(point);
			}
			if (cursor_member(cursor)->imageColor.a())
			{
				int x = point.x() - origin.x();
				int y = point.y() - origin.y();
				int w = size.width();
				int h = size.height();
				painter.setColor(cursor_member(cursor)->imageColor);
				painter.drawImage(x, y, w, h, image);
			}
		}
	}
}
void IWindow::drawDrag()
{
	if (BDrag* drag = dragBoard.drag)
	{
		if (dragBoard.dirty)
		{
			dragBoard.fresh();
			dragBoard.frame(msfbo, GL_COLOR_ATTACHMENT2);
			dragBoard.dirty = false;
		}

		BPoint globalPos = boss->globalPos();
		BPoint cursorPos = BCursor::GetPos();
		BPoint pos = cursorPos - globalPos;
		BPoint offset = drag->offset();
		const BValue& offsetValue = dragBoard.findValue(Value_Offset);
		if (offsetValue.valid())
			offset = offsetValue;
		pos += offset;

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		int width = widget_member(boss)->size.width();
		int height = widget_member(boss)->size.height();
		glViewport(0, 0, width, height);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, width, height, 0, -1, 1);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		glEnable(GL_BLEND);
		glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
		glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
		glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);

		glEnable(GL_TEXTURE_2D);
		BRect area = dragBoard.area + pos;
		glColor4ub(255, 255, 255, 255);
		glBindTexture(GL_TEXTURE_2D, dragBoard.tbo);
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(area.left(), area.top());
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(area.left(), area.bottom());
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(area.right(), area.bottom());
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(area.right(), area.top());
		glEnd();
		glBindTexture(GL_TEXTURE_2D, 0);
		glDisable(GL_TEXTURE_2D);
	}
}
void IWindow::drawTips()
{
	if (hoverWidget && tipsBoard.visible && hoverWidget->realTips().size())
	{
		if (tipsBoard.dirty)
		{
			tipsBoard.fresh();
			tipsBoard.frame(msfbo, GL_COLOR_ATTACHMENT2);
			tipsBoard.dirty = false;
		}

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		int width = widget_member(boss)->size.width();
		int height = widget_member(boss)->size.height();
		glViewport(0, 0, width, height);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, width, height, 0, -1, 1);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		glEnable(GL_BLEND);
		glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
		glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
		glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);

		glEnable(GL_TEXTURE_2D);
		const BRect& area = tipsBoard.area;
		glColor4ub(255, 255, 255, 255);
		glBindTexture(GL_TEXTURE_2D, tipsBoard.tbo);
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(area.left(), area.top());
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(area.left(), area.bottom());
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(area.right(), area.bottom());
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(area.right(), area.top());
		glEnd();
		glBindTexture(GL_TEXTURE_2D, 0);
		glDisable(GL_TEXTURE_2D);
	}
}

bool IWindow::isFullScreen()
{
	if (BScreen* screen = BSystem::GetScreen(0))
	{
		BSize winSize = getWindowSize();
		BSize screenSize = screen->size();
		return screenSize == winSize;
	}
	return false;
}
void IWindow::focus(BWidget* widget)
{
	if (widget && (!widget_member(widget)->state.enabled || !widget->focusable()))
		widget = 0;
	BWidget* out = 0;
	BWidget* in = 0;
	if (focusWidget != widget)
	{
		out = focusWidget;
		focusWidget = widget;
		in = focusWidget;
		refresh = true;
	}
	BEvent activeEvent(Event_Active);
	BEvent focusEvent(Event_Focus);
	if (out)
	{
		activeEvent.setValue(false);
		focusEvent.setValue(false);
		BWidget* widget = out;
		while (widget && widget != in)
		{
			if (widget->above(in))
				break;
			widget_member(widget)->state.actived = false;
			widget_member(widget)->procEvent(activeEvent);
			widget = widget_member(widget)->parent;
		}
		widget_member(out)->focused = false;
		widget_member(out)->procEvent(focusEvent);
	}
	if (in)
	{
		if (popupWidgets.contain(in) && popupWidgets.last() != in)
		{
			popupWidgets.remove(in);
			popupWidgets.append(in);
		}
		activeEvent.setValue(true);
		focusEvent.setValue(true);
		BWidget* widget = in;
		while (widget)
		{
			if (widget_member(widget)->state.actived)
				break;
			widget_member(widget)->state.actived = true;
			widget_member(widget)->procEvent(activeEvent);
			widget = widget_member(widget)->parent;
		}
		widget_member(in)->focused = true;
		widget_member(in)->procEvent(focusEvent);
		if (focusEvent.result().is<BPoint>())
		{
			const BPoint& pos = focusEvent.result();
			BPoint abPos = focusWidget->absolutePos() + pos;
			setInputPoint(abPos);
		}
	}
}
bool IWindow::popup(BWidget* widget, bool flag)
{
	if (flag)
	{
		if (!popupWidgets.contain(widget))
		{
			popupWidgets.append(widget);
			BEvent popupEvent(Event_Popup, true);
			widget_member(widget)->procEvent(popupEvent);
			return true;
		}
	}
	else if (popupWidgets.remove(widget))
	{
		BEvent popupEvent(Event_Popup, false);
		widget_member(widget)->procEvent(popupEvent);
		for (int i = 0; i < popupWidgets.size(); i++)
		{
			BWidget* pwidget = popupWidgets[i];
			if (pwidget->under(widget))
			{
				if (focusWidget == pwidget)
					focus(0);
				popupWidgets.remove(i--);
				widget_member(widget)->procEvent(popupEvent);
			}
		}
		refresh = true;
		return true;
	}
	return false;
}

BWidget* IWindow::catchWidget(const BPoint& point)
{
	if (popupWidgets.size())
	{
		for (int i = popupWidgets.size() - 1; i >= 0; i--)
		{
			BWidget* widget = popupWidgets[i];
			if (!widget_member(widget)->visible)
				continue;
			if (widget_member(widget)->ignored())
				continue;
			BWidget* result = findPopupWidget(widget, point, boss->size());
			if (result)
				return result;
			if (widget->modal())
				return 0;
		}
	}
	return catchWidget(boss, point, boss->size());
}
BWidget* IWindow::catchWidget(BWidget* widget, const BPoint& point, const BRect& clip)
{
	BRect rect = widget->rect();
	if (boss == widget)
		rect.setPos(0);
	if (!widget->catchPoint(point - rect.pos()))
		return 0;
	BRect clipRect = rect & clip;
	if (!clipRect.contain(point))
		return 0;
	currentRect = clipRect;
	BPoint localPoint = point - rect.pos();
	BWidgetHolderArray& children = widget_member(widget)->children;
	for (int i = children.size() - 1; i >= 0; i--)
	{
		BWidget* child = children[i];
		if (!widget_member(child)->visible)
			continue;
		if (widget_member(child)->ignored())
			continue;
		if (widget_member(child)->floating)
		{
			BWidget* ret = catchWidget(child, localPoint, widget->size());
			if (ret)
				return ret;
		}
		else
		{
			const BRect& centerRect = widget_member(widget)->rectCenter;
			BWidget* ret = catchWidget(child, localPoint, centerRect);
			if (ret)
				return ret;
		}
	}
	return widget;
}
BWidget* IWindow::findPopupWidget(BWidget* widget, const BPoint& point, const BRect& clip)
{
	if (!widget_member(widget)->state.enabled)
		return 0;

	BRect rect = widget->rect();

	BRect viewRect = widget->absoluteRect() & clip;
	if (!viewRect.contain(point))
		return 0;

	BWidgetHolderArray& children = widget_member(widget)->children;
	for (int i = children.size() - 1; i >= 0; i--)
	{
		BWidget* child = widget->child(i);
		if (!widget_member(child)->visible)
			continue;
		if (widget_member(child)->ignored())
			continue;
		if (widget_member(child)->floating)
		{
			BWidget* ret = findPopupWidget(child, point, viewRect);
			if (ret)
				return ret;
		}
		else
		{
			BRect clientViewRect = widget->centerRect();
			clientViewRect += viewRect.pos();
			BWidget* ret = findPopupWidget(child, point, clientViewRect);
			if (ret)
				return ret;
		}
	}

	return widget;
}

int CheckShortcut(BShortcut* shortcut, const BBoolArray& keyStates)
{
	const BArray<Key>& keys = shortcut_member(shortcut)->keys;
	if (keys.empty())
		return 0;
	int count = 0;
	for (int i = 0; i < keys.size(); i++)
	{
		Key key = keys[i];
		bool result = keyStates[key];
		if (result)
			count++;
		int more = 0;
		if (key == Key_Alt && keyStates[Key_LAlt])
			more++;
		if (key == Key_Alt && keyStates[Key_RAlt])
			more++;
		if ((key == Key_LAlt || key == Key_RAlt) && keyStates[Key_Alt])
			more++;
		if (key == Key_Ctrl && keyStates[Key_LCtrl])
			more++;
		if (key == Key_Ctrl && keyStates[Key_RCtrl])
			more++;
		if ((key == Key_LCtrl || key == Key_RCtrl) && keyStates[Key_Ctrl])
			more++;
		if (key == Key_Shift && keyStates[Key_LShift])
			more++;
		if (key == Key_Shift && keyStates[Key_RShift])
			more++;
		if ((key == Key_LShift || key == Key_RShift) && keyStates[Key_Shift])
			more++;
		if (!result && !more)
			return 0;
		count += more;
	}
	return count;
}
void IWindow::procShortcut()
{
	if (app_shortcuts.empty())
		return;
	int count = keyStates.count(true);
	BWidget* widget = this->focusWidget;
	while (widget)
	{
		for (auto it = app_shortcuts.begin(); it.valid(); ++it)
		{
			BShortcut* shortcut = *it;
			if (shortcut_member(shortcut)->widget != widget)
				continue;
			if (count == CheckShortcut(shortcut, keyStates))
			{
				shortcut->trigger();
				return;
			}
		}
		widget = widget_member(widget)->parent;
	}
	for (auto it = app_shortcuts.begin(); it.valid(); ++it)
	{
		BShortcut* shortcut = *it;
		if (shortcut_member(shortcut)->widget)
			continue;
		if (count == CheckShortcut(shortcut, keyStates))
		{
			shortcut->trigger();
			return;
		}
	}
}
void IWindow::procKeyPress(BKeyEvent& keyEvent)
{
	event_member(&keyEvent)->window = this;
	BWidget* widget = focusWidget;
	while (widget)
	{
		widget_member(widget)->procEvent(keyEvent);
		if (keyEvent.accepted())
			return;
		widget = widget->parent();
	}
	this->procShortcut();
}
void IWindow::procKeyRelease(BKeyEvent& keyEvent)
{
	event_member(&keyEvent)->window = this;
	BWidget* widget = focusWidget;
	while (widget)
	{
		widget_member(widget)->procEvent(keyEvent);
		if (keyEvent.accepted())
			return;
		widget = widget->parent();
	}
}
void IWindow::procMousePress(BMouseEvent& mouseEvent)
{
	mouse_button = mouseEvent.button();
	mouse_pressed = true;
	mouse_pressed_time = mouseEvent.time();
	mouse_pressed_pos = mouseEvent.absolutePos();
	mouse_dragging = false;

	BWidget* widget = catchWidget(mouse_pressed_pos);
	while (widget && !object_member(widget)->enabled)
	{
		widget = widget_member(widget)->parent;
	}
	this->focus(widget);
	if (focusWidget)
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
		widget_member(focusWidget)->procEvent(mouseEvent);
	}

	if (hoverCursor && hoverCursor->type() == Cursor_Ibeam)
	{
		setInputPoint(mouseEvent.absolutePos());
	}
}
void IWindow::procMouseRelease(BMouseEvent& mouseEvent)
{
	mouse_button = Button_None;
	mouse_pressed = false;
	mouse_dragging = false;
	if (focusWidget)
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
		widget_member(focusWidget)->procEvent(mouseEvent);
	}
	if (focusWidget && hoverWidget && focusWidget == hoverWidget && mouse_click_time > 0.0)
	{
		int distance = (mouseEvent.absolutePos() - mouse_click_pos).length();
		BReal interval = mouseEvent.time() - mouse_click_time;
		if (distance < 5 && interval < app_clickInterval)
		{
			mouseEvent.reset(Event_DoubleClick);
			mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
			widget_member(focusWidget)->procEvent(mouseEvent);
			mouse_click_time = 0;
			return;
		}
	}
	if (focusWidget && mouse_pressed_time > 0.0)
	{
		int distance = (mouseEvent.absolutePos() - mouse_pressed_pos).length();
		BReal interval = mouseEvent.time() - mouse_pressed_time;
		if (distance < 5 && interval < app_clickInterval)
		{
			mouseEvent.reset(Event_MouseClick);
			mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
			widget_member(focusWidget)->procEvent(mouseEvent);
			mouse_click_time = mouseEvent.time();
			mouse_click_pos = mouseEvent.absolutePos();
		}
	}
}
void IWindow::procMouseMove(BMouseEvent& mouseEvent)
{
	if (focusWidget && mouse_pressed)
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
		widget_member(focusWidget)->procEvent(mouseEvent);
		int distance = (mouseEvent.absolutePos() - mouse_pressed_pos).length();
		if (!mouse_dragging && distance >= 5)
		{
			mouse_dragging = true;
			mouse_event = mouseEvent;
			mouse_event.reset(Event_MouseDrag);
			mouse_event.setPos(mouseEvent.absolutePos() - focusWidget->absolutePos());
			widget_member(focusWidget)->procEvent(mouse_event);
		}
	}
	else
	{
		BPoint absolutePos = mouseEvent.absolutePos();
		BWidget* widget = catchWidget(absolutePos);
		if (hoverWidget != widget)
		{
			mouse_click_time = 0;
			BWidget* aboveWidget = widget;
			if (widget && widget_member(widget)->state.enabled)
			{
				mouse_event = mouseEvent;
				mouse_event.reset(Event_MouseEnter);
				mouse_event.setPos(mouseEvent.absolutePos() - widget->absolutePos());
				widget_member(widget)->procEvent(mouse_event);

				mouse_event.reset(Event_Hover, true);
				while (aboveWidget && !widget_member(aboveWidget)->state.hovered)
				{
					widget_member(aboveWidget)->state.hovered = true;
					widget_member(aboveWidget)->procEvent(mouse_event);
					aboveWidget = widget_member(aboveWidget)->parent;
				}
			}
			if (hoverWidget)
			{
				mouse_event = mouseEvent;
				mouse_event.reset(Event_MouseLeave);
				mouse_event.setPos(mouseEvent.absolutePos() - hoverWidget->absolutePos());
				widget_member(hoverWidget)->procEvent(mouse_event);

				mouse_event.reset(Event_Hover, false);
				while (hoverWidget && aboveWidget != hoverWidget)
				{
					widget_member(hoverWidget)->state.hovered = false;
					widget_member(hoverWidget)->procEvent(mouse_event);
					hoverWidget = widget_member(hoverWidget)->parent;
				}
			}
			hoverWidget = widget;
			refresh = true;
		}
		tipsBoard.reset(widget);
		tipsBoard.pos = absolutePos;
		if (hoverWidget && widget_member(hoverWidget)->state.enabled)
		{
			mouseEvent.setPos(mouseEvent.absolutePos() - hoverWidget->absolutePos());
			widget_member(hoverWidget)->procEvent(mouseEvent);
		}
	}
	BCursor* cursor = hoverWidget ? hoverWidget->cursor() : 0;
	if (hoverCursor != cursor)
	{
		hoverCursor = cursor;
		refresh = true;
	}
	if (hoverCursor && hoverCursor->type() == Cursor_None)
	{
		refresh = true;
	}
	freshCursor();
}
void IWindow::procMouseWheel(BMouseEvent& mouseEvent)
{
	if (hoverWidget && widget_member(hoverWidget)->state.enabled)
	{
		mouseEvent.setPos(mouseEvent.absolutePos() - hoverWidget->absolutePos());
		BWidget* widget = hoverWidget;
		while (widget)
		{
			widget_member(widget)->procEvent(mouseEvent);
			if (mouseEvent.accepted())
				return;
			widget = widget->parent();
			if (!widget || !widget->absoluteRect().contain(mouseEvent.absolutePos()))
				break;
			mouseEvent.setPos(mouseEvent.pos() + widget->pos());
		}
	}
}
void IWindow::procDragMove(BMouseEvent& mouseEvent)
{
	dragBoard.drag = mouseEvent.value();

	BPoint absolutePos = mouseEvent.absolutePos();
	if (!boss->absoluteRect().contain(absolutePos))
		dragBoard.drag = 0;
	if (!dragBoard.drag)
		return;

	BWidget* widget = catchWidget(absolutePos);
	if (hoverWidget != widget)
	{
		mouse_click_time = 0;
		if (hoverWidget)
		{
			mouseEvent.reset(Event_DragLeave);
			mouseEvent.setPos(mouseEvent.absolutePos() - hoverWidget->absolutePos());
			dragBoard.drag->setPos(mouseEvent.pos());
			widget_member(hoverWidget)->procEvent(mouseEvent);
		}
		if (widget && widget_member(widget)->state.enabled)
		{
			mouseEvent.reset(Event_DragEnter);
			mouseEvent.setPos(mouseEvent.absolutePos() - widget->absolutePos());
			dragBoard.drag->setPos(mouseEvent.pos());
			widget_member(widget)->procEvent(mouseEvent);
		}
		hoverWidget = widget;
	}
	dragBoard.reset(widget);
	if (hoverWidget && widget_member(hoverWidget)->state.enabled)
	{
		mouseEvent.reset(Event_DragMove);
		mouseEvent.setPos(mouseEvent.absolutePos() - hoverWidget->absolutePos());
		dragBoard.drag->setPos(mouseEvent.pos());
		widget_member(hoverWidget)->procEvent(mouseEvent);
	}
	hoverCursor = hoverWidget ? hoverWidget->cursor() : 0;
	refresh = true;
	freshCursor();
}
void IWindow::procDragDrop(BMouseEvent& mouseEvent)
{
	if (hoverWidget)
	{
		widget_member(hoverWidget)->procEvent(mouseEvent);
	}
	dragBoard.clear();
	refresh = true;
}
